package com.example.usbhidtool.hid

import android.content.Context
import android.util.Log
import com.example.usbhidtool.HidPageActivity
import com.example.usbhidtool.hid.umessage.UMessage
import com.example.usbhidtool.hid.umessage.UMessageCallBack
import com.example.usbhidtool.hid.umessage.UMessageWithCallBack
import com.example.usbhidtool.utils.HidUtil
import com.example.usbhidtool.utils.TimeUtil
import com.hdphone.zljutils.ZljUtils
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.util.concurrent.ConcurrentLinkedQueue
import kotlin.concurrent.thread

/**
 * HID消息管理
 *
 *
 * 用来发送各种消息,获取消息发送结果
 */
class HidMsgManager(val context: Context) {

    //通用的消息队列，用于发送命令，普通数据
    private val comMQ: ConcurrentLinkedQueue<UMessage> = ConcurrentLinkedQueue<UMessage>()

    //文件的消息队列，用户发送文件数据
    private val fileMQ: ConcurrentLinkedQueue<UMessage> = ConcurrentLinkedQueue<UMessage>()

    //日志输出器
    private var logWriter: ((String, isSend: Boolean) -> Unit)? = null

    //文件发送完成的回调，最后一个包封装成带回调的包
    private var finishCallBack: ((hasFinish: Boolean) -> Unit)? = null


    /**
     * 待发送文件的总包数
     * 当文件队列为空的时候，重置为0，也就是作为文件是否传输完的标记
     */
    private var mTotalPack: Int = 0

    //设置日志输出器
    fun setLogPrinter(logWriter: (String, isSend: Boolean) -> Unit) {
        this.logWriter = logWriter
    }


    fun setSendFileFinishCallBack(finishCallBack: (hasFinish: Boolean) -> Unit) {
        this.finishCallBack = finishCallBack
    }

    /**
     * 当前是否正在发送文件
     * 当发送文件的时候，不能发送长消息，只能发送命令
     */
    fun isFileSending(): Boolean {
        return !fileMQ.isEmpty()
    }

    fun getSize(): Int {
        return comMQ.size + fileMQ.size
    }


    fun resetTotalPack() {
        mTotalPack = 0
    }

    fun getTotalPack(): Int {
        return mTotalPack
    }


    /**
     * 每次取一个消息
     */
    fun getOneMessage(): UMessage? {
        //命令消息优先发送，
        if (!comMQ.isEmpty()) {
            return comMQ.poll()
        } else {
            if (!fileMQ.isEmpty()) {
                return fileMQ.poll()
            }
        }
        return null
    }

    /**
     * 获取命令消息
     */
    fun sendCmdMsg(cmd: String?, callBack: UMessageCallBack? = null) {
        if (cmd.isNullOrEmpty()) {
            ZljUtils.TOAST().showShortToast("无效命令")
            return
        }
        logWriter?.invoke("准备发送命令：$cmd", true)
        if (callBack == null) {
            comMQ.add(UMessage(DataPack.cmdPack(cmd)))
        } else {
            val msg = UMessageWithCallBack(DataPack.cmdPack(cmd))
            msg.callBack = callBack
            comMQ.add(msg)
        }
    }


    /**
     * 获取文件头消息
     */
    fun sendHeaderMsg(header: ByteArray, length: Int, isFile: Boolean, callBack: UMessageCallBack? = null) {
        val headerData = DataPack.infoPack(isFile, length, header)
        logWriter?.invoke("准备数据头信息", true)
        if (callBack == null) {
            comMQ.add(UMessage(headerData))
        } else {
            val msg = UMessageWithCallBack(headerData)
            msg.callBack = callBack
            comMQ.add(msg)
        }
    }

    /**
     * 这里区分是否是文件数据
     */
    fun sendInfoMsg(data: ByteArray?, isFile: Boolean) {
        if (isFile) {
            fileMQ.add(UMessage(DataPack.dataPack(data)))
        } else {
            comMQ.add(UMessage(DataPack.dataPack(data)))
        }
    }


    //发送数据信息
    fun sendDataMsg(data: ByteArray, callBack: UMessageCallBack? = null) {
        sendHeaderMsg(data, data.size, false, callBack = {
            if (it == 1) {
                //头发送成功后
                logWriter?.invoke("数据头信息发送成功", true)
                logWriter?.invoke("开始发送数据", true)
                //总大小
                val totalSize = data.size
                //待发送的大小
                var remainSize = totalSize
                //当前发送包大小
                var sendSize = 0
                while (remainSize > 0) {
                    sendSize = if (remainSize > 508) 508 else remainSize
                    val sendData = ByteArray(sendSize)
                    System.arraycopy(data, totalSize - remainSize, sendData, 0, sendSize)
                    sendInfoMsg(sendData, false)
                    remainSize -= sendSize
                }
                logWriter?.invoke("数据发送完成", true)
            } else {
                logWriter?.invoke("数据头信息发送失败", true)
                callBack?.onComplete(-1)
            }
        })
    }

    /**
     * 外部调用的发送文件
     */
    fun sendFile(fileName: String, md5: String) {
        //获取文件大小
        val manager = context.assets
        val intpuStream = manager.open(fileName)
        // 获取文件的字节数
        val fileLength = intpuStream.available()
        val last = fileLength % 508 //最后一个
        var packSize = fileLength / 508
        if (last != 0) {
            packSize += 1
        }
        logWriter?.invoke("包总数：$packSize", true)
        intpuStream.close()
        logWriter?.invoke("文件大小：$fileLength，文件名：$fileName", true)
        val nameBytes = fileName.toByteArray()
        val infoBytes = ByteArray(288)
        //复制名字到infoBytes,0-255，不超过
        System.arraycopy(nameBytes, 0, infoBytes, 0, nameBytes.size)
        val md5Bytes = md5.toByteArray()
        //复制MD5，占32字节
        System.arraycopy(md5Bytes, 0, infoBytes, 256, md5Bytes.size)
        //todo header里面加入MD5信息
        logWriter?.invoke("准备发送文件头信息", true)
        sendHeaderMsg(infoBytes, fileLength, true) {
            if (it == 1) {
                //文件头发送成功
                logWriter?.invoke("文件头信息发送成功", true)
                sendFileReal(fileName, fileLength.toLong())
            }
        }
    }

    /**
     * 真正的发送文件
     */
    private fun sendFileReal(fileName: String, fileLength: Long) {
        thread {
            val manager = context.assets
            try {
                //通过流循环读取
                val inputStream = manager.open(fileName)
                val bins = BufferedInputStream(inputStream)
                //一次读取508字节的数据
                val buffer = ByteArray(HidPageActivity.MAXPacketSize - 4)
                var bytesRead = 0
                logWriter?.invoke("文件读写开始", true)
                logWriter?.invoke("开始发送文件片段,1秒打印一次", true)
                var readTimes = 0
                val startTime = System.currentTimeMillis()
                var lastTime = System.currentTimeMillis()
                while (bins.read(buffer).also { bytesRead = it } != -1) {
                    //如果队列数据大于1000，则休眠1000毫秒
                    if (fileMQ.size > 10000) {
                        try {
                            Thread.sleep(10)
                        } catch (e: java.lang.Exception) {
                            e.printStackTrace()
                        }
                    }
                    readTimes++
                    if (System.currentTimeMillis() - lastTime > 1000) {
                        lastTime = System.currentTimeMillis()
                        logWriter?.invoke("$readTimes 个文件片段已加入队列", true)
                    }
                    if (bytesRead == HidPageActivity.MAXPacketSize - 4) {
                        val uMessage = UMessage(DataPack.dataPack(buffer))
                        fileMQ.add(uMessage)
                    } else {
                        //最后一个包
                        val lastPack = ByteArray(bytesRead)
                        System.arraycopy(buffer, 0, lastPack, 0, bytesRead)
                        val uMessage = UMessageWithCallBack(DataPack.dataPack(lastPack))
                        val times = readTimes
                        uMessage.setCallBack { sendSuccess: Int ->
                            if (sendSuccess == 1) {
                                logWriter?.invoke("文件最后片段发送成功,length:${lastPack.size}", true)
                                logWriter?.invoke("===文件传输完成===\n", true)
                                logWriter?.invoke("文件读写次数：$times", true)
                                val endtime = System.currentTimeMillis()
                                val useTime = (endtime - startTime) / 1000
                                val rate = fileLength / 1024 / useTime
                                logWriter?.invoke("文件传输耗时：$useTime 秒", true);
                                logWriter?.invoke("文件传输速率：$rate kb/s", true)
                            }
                        }
                        fileMQ.add(uMessage)
                    }
                }
                bins.close()
                inputStream.close()
            } catch (e: java.lang.Exception) {
                logWriter?.invoke("发送文件异常$e.message", true)
            }
        }
    }


    /**
     * 从外部存储目录发送文件
     */
    fun sendFileFromSdCard(appName: String, filePath: String, md5: String): Boolean {
        //获取文件大小
        try {
            val intpuStream = FileInputStream(File(filePath))
            // 获取文件的字节数
            val fileLength = intpuStream.available()
            intpuStream.close()
            logWriter?.invoke("包总数：$mTotalPack", true)
            logWriter?.invoke("文件大小：$fileLength，文件名：$appName", true)
            val nameBytes = appName.toByteArray()
            val infoBytes = ByteArray(288)
            //复制名字到infoBytes,0-255，不超过
            System.arraycopy(nameBytes, 0, infoBytes, 0, nameBytes.size)
            val md5Bytes = md5.toByteArray()
            //复制MD5，占32字节
            System.arraycopy(md5Bytes, 0, infoBytes, 256, md5Bytes.size)
            //todo header里面加入MD5信息
            logWriter?.invoke("准备发送文件头信息", true)
            sendHeaderMsg(infoBytes, fileLength, true) {
                if (it == 1) {
                    //文件头发送成功
                    logWriter?.invoke("文件头信息发送成功", true)
                    sendFileRealFromSdCard(filePath, fileLength.toLong())
                }
            }
            return true
        } catch (e: Exception) {
            return false
        }
    }

    /**
     * 真正从SD卡发送文件的逻辑
     */
    private fun sendFileRealFromSdCard(fileName: String, fileLength: Long) {
        thread {
            try {
                //通过流循环读取
                val inputStream = FileInputStream(fileName)
                val bins = BufferedInputStream(inputStream)
                //一次读取508字节的数据
                val buffer = ByteArray(HidContants.MAX_PACKET_SIZE - 4)
                var bytesRead = 0
                logWriter?.invoke("文件读写开始", true)
                logWriter?.invoke("开始发送文件片段,1秒打印一次", true)
                var readTimes = 0
                val startTime = System.currentTimeMillis()
                var lastTime = System.currentTimeMillis()
                while (bins.read(buffer).also { bytesRead = it } != -1) {
                    //如果队列数据大于1000，则休眠1000毫秒
                    if (fileMQ.size > 10000) {
                        try {
                            Thread.sleep(10)
                        } catch (e: java.lang.Exception) {
                            e.printStackTrace()
                        }
                    }
                    readTimes++
                    if (System.currentTimeMillis() - lastTime > 1000) {
                        lastTime = System.currentTimeMillis()
                        logWriter?.invoke("$readTimes 个文件片段已加入队列", true)
                    }
                    if (bytesRead == HidContants.MAX_PACKET_SIZE - 4) {
                        //判断是否是最后一个包
                        if (readTimes == mTotalPack) {
                            val uMessage = UMessageWithCallBack(DataPack.dataPack(buffer))
                            uMessage.setCallBack { sendSuccess: Int ->
                                if (sendSuccess == 1) {
                                    //发送完成的回调
                                    finishCallBack?.invoke(true)
                                    logWriter?.invoke("文件最后片段发送成功,length:504", true)
                                    logWriter?.invoke("===文件传输完成===\n", true)
                                    logWriter?.invoke("文件读写次数：$readTimes", true)
                                    val endtime = System.currentTimeMillis()
                                    val useTime = (endtime - startTime) / 1000
                                    val rate = fileLength / 1024 / useTime
                                    logWriter?.invoke("文件传输耗时：$useTime 秒", true);
                                    logWriter?.invoke("文件传输速率：$rate kb/s", true)
                                }
                            }
                        } else {
                            val uMessage = UMessage(DataPack.dataPack(buffer))
                            fileMQ.add(uMessage)
                        }

                    } else {
                        //最后一个包
                        val lastPack = ByteArray(bytesRead)
                        System.arraycopy(buffer, 0, lastPack, 0, bytesRead)
                        val uMessage = UMessageWithCallBack(DataPack.dataPack(lastPack))
                        val times = readTimes
                        uMessage.setCallBack { sendSuccess: Int ->
                            if (sendSuccess == 1) {
                                //发送进度100的回调
                                finishCallBack?.invoke(true)
                                logWriter?.invoke("文件最后片段发送成功,length:${lastPack.size}", true)
                                logWriter?.invoke("===文件传输完成===\n", true)
                                logWriter?.invoke("文件读写次数：$times", true)
                                val endtime = System.currentTimeMillis()
                                val useTime = (endtime - startTime) / 1000
                                val rate = fileLength / 1024 / useTime
                                logWriter?.invoke("文件传输耗时：$useTime 秒", true);
                                logWriter?.invoke("文件传输速率：$rate kb/s", true)
                            }
                        }
                        fileMQ.add(uMessage)
                    }
                }
                bins.close()
                inputStream.close()
            } catch (e: java.lang.Exception) {
                logWriter?.invoke("发送文件异常$e.message", true)
            }
        }
    }

    /**
     * 设备拔出后，重置状态
     * 清空队列
     */
    fun reset() {
        fileMQ.clear()
        comMQ.clear()
    }

}